
#include <string.h>
#include <CoOS.h>			 			/*!< CooCox RTOS header file.		  */
#include "Driver\DrvADC.h"
#include "Driver\DrvSYS.h"
#include "Driver\DrvTIMER.h"
#include "Driver\DrvPWM.h"
#include "Driver\DrvGPIO.h"


#include "ir_remo_ctrl.h"
#include "usb_vcom.h"

#include "flash_param.h"

#include "temperature.h"



typedef uint32_t param_t;

OS_STK   task_temp_stk[TASK_STK_SIZE];	 	/*!< Stack of 'task_init' task.	  */
static 	 uint16_t g_pwm_cycle = 0;			// save pwm cycle

static int16_t	adc_fifo[WIN_SIZE][MAX_CHANNEL];
static int32_t	adc_acc[MAX_CHANNEL];
static int16_t	temperature[MAX_CHANNEL];
static uint8_t  cur_fifo_ptr = 0;

static uint8_t	manual_fan_power[MAX_CHANNEL] = {0xFF, 0xFF};

static OS_FlagID	flag_adc = 0;

static const uint8_t 	adc_channel_map[4] = {
	3, 4, 	// ch0, ch1
	5, 6,	// ch2, ch3
};

static const uint8_t	pwm_channel_map[4] = {
	3, 2,	// ch0, ch1
	0, 1,	// ch2, ch3
};

 /*
 *********************************************************************************************
 * @brief		Configurate and intialize timer to 
 * @param[in]	None
 * @param[out]	None
 * @retval		None
 * @details	 	This fucntion is called to enable RTC interrupt.
 *********************************************************************************************
 */
void Timer0Config()
{
	DrvTIMER_Init();
	// Clock source for Timer
    // Select external 32KHz for Timer 0
    DrvSYS_SelectIPClockSource(E_SYS_TMR0_CLKSRC, 1);
	DrvTIMER_Open(E_TMR0, 2, E_PERIODIC_MODE);
}

void AdcIntCallback(uint32_t u32UserData);


void AdcConfig(void)
{
	DrvGPIO_InitFunction(E_FUNC_ADC3);
	DrvGPIO_InitFunction(E_FUNC_ADC4);
	DrvGPIO_InitFunction(E_FUNC_ADC5);
	DrvGPIO_InitFunction(E_FUNC_ADC6);

	DrvADC_Open(
		ADC_SINGLE_END, ADC_SINGLE_CYCLE_OP, 
		0x18, 					// channel 3, 4
		INTERNAL_HCLK, 1);
	DrvADC_EnableADCInt(AdcIntCallback, 0);

	DrvADC_EnableSelfCalibration();
	while (!DrvADC_IsCalibrationDone() );
	DrvADC_DisableSelfCalibration();
}

void PwmConfig(void)
{
	static S_DRVPWM_TIME_DATA_T	pt2, pt3;
    DrvPWM_Open();  
    DrvGPIO_InitFunction(E_FUNC_PWM2);
    DrvGPIO_InitFunction(E_FUNC_PWM3);
//	DrvGPIO_InitFunction(E_FUNC_PWM01);

	pt2.u8Mode = DRVPWM_AUTO_RELOAD_MODE;
    pt2.u32Frequency = 5000;         /* 100K Hz */
    pt2.u8HighPulseRatio = 50;      /* High Pulse peroid : Total Pulse peroid = 30 : 100 */ 
    pt2.i32Inverter = 0; 
	   
	DrvPWM_SelectClockSource(DRVPWM_TIMER2, DRVPWM_EXT_12M);
   	DrvPWM_SetTimerIO(DRVPWM_TIMER2, 1);
    DrvPWM_Enable(DRVPWM_TIMER2, 1);  

	DrvPWM_SetTimerClk(DRVPWM_TIMER2, &pt2);

	pt3.u8Mode = DRVPWM_AUTO_RELOAD_MODE;
    pt3.u32Frequency = 5000;         /* 100K Hz */
    pt3.u8HighPulseRatio = 50;      /* High Pulse peroid : Total Pulse peroid = 30 : 100 */ 
    pt3.i32Inverter = 0; 
	   
	DrvPWM_SelectClockSource(DRVPWM_TIMER3, DRVPWM_EXT_12M);
   	DrvPWM_SetTimerIO(DRVPWM_TIMER3, 1);
    DrvPWM_Enable(DRVPWM_TIMER3, 1);  

	DrvPWM_SetTimerClk(DRVPWM_TIMER3, &pt3);


	g_pwm_cycle = DrvPWM_GetTimerCounter(2);
}

void AdcIntCallback(uint32_t u32UserData)
{
	uint8_t ch = 0;
	uint8_t ch_mask = 1;

	int16_t * buf = adc_fifo[cur_fifo_ptr];
	for ( ; ch < MAX_CHANNEL; ++ch, ch_mask <<= 1)
	{
//		if ( g_channel_enabled & ch_mask)
		buf[ch] = DrvADC_GetConversionData(adc_channel_map[ch]);
	}

	CoEnterISR();
	isr_SetFlag(flag_adc);
	CoExitISR();
}


void adc_timer0_callback(uint32_t data)
{
#ifdef ENABLE_TIMING_CALB
	static int test = 0;
	if ( 0 == test )	DrvGPIO_ClrBit(E_GPB, 0);
	else				DrvGPIO_SetBit(E_GPB, 0);
	test ^= 1;
#endif

	DrvADC_StartConvert();
}

int16_t adc2temp(int32_t val, uint8_t ch)
{
	val *= g_scale[ch].p;
	val >>= g_scale[ch].s;
	val += g_scale[ch].q;
	return (int16_t)val;
}

int16_t temp2amp(int16_t temp, uint8_t ch)
{
	int16_t 	t0, amp;
	t0 = g_scale[ch].t_on << 4;
	if (temp > t0)	amp = ((temp - t0) * g_scale[ch].k) >> 8;
	else	amp = 0;
	if (amp > 0x10) amp = 0x10;

	return amp;
}

void update_pwm(int16_t amp, uint8_t ch)
{
	uint16_t 	pwm_duty;
	uint8_t 	pwm_channel;
	
	pwm_duty = ((uint32_t)(amp) * g_pwm_cycle) >> 4 ;

	pwm_channel = pwm_channel_map[ch];
	switch (pwm_channel)
	{
	case 0:		PWMA->CMR0 = pwm_duty;	break;		
	case 1:		PWMA->CMR1 = pwm_duty;	break;
	case 2:		PWMA->CMR2 = pwm_duty;	break;
	case 3:		PWMA->CMR3 = pwm_duty;	break;
	}
}


void task_temperature(void * pdata)
{
//-- Definations
	uint8_t ii;
	uint8_t ch = 0;
	uint8_t ch_mask = 1;
	static uint32_t event_timer0;
	uint8_t next_fifo_ptr;


//-- Code
	// Initialize buffer
	for (ii = 0; ii < WIN_SIZE; ++ii)
	{
		memset(adc_fifo[ii], 0, sizeof(int16_t) * MAX_CHANNEL);
	}
	memset(adc_acc, 0, sizeof(int32_t) * MAX_CHANNEL);
	memset(manual_fan_power, 0xFF, sizeof(uint8_t) * MAX_CHANNEL);
	 
	load_scales();
	CoTickDelay(1000);

	flag_adc = CoCreateFlag(1, 0);

	// Start timer 0
	event_timer0 = DrvTIMER_SetTimerEvent(E_TMR0, 1, adc_timer0_callback, 0);
	DrvTIMER_EnableInt(E_TMR0);
	DrvTIMER_Start(E_TMR0);

	while (1)
	{
		next_fifo_ptr = cur_fifo_ptr + 1;
		if (next_fifo_ptr >= WIN_SIZE) next_fifo_ptr = 0; 
		
		CoWaitForSingleFlag(flag_adc, 0);

		for (ch = 0, ch_mask = 1; ch < MAX_CHANNEL; ++ch, ch_mask <<= 1)
		{
//			if ( !(g_channel_enabled & ch_mask) ) continue;
			CoSchedLock();
			// running average
			adc_acc[ch] -= adc_fifo[next_fifo_ptr][ch];
			adc_acc[ch] += adc_fifo[cur_fifo_ptr][ch];
			temperature[ch] = adc2temp(adc_acc[ch] >> WIN_POWER, ch);
			CoSchedUnlock();

			// update PWM
			if ( manual_fan_power[ch] & 0x80 )
			{
				update_pwm(temp2amp(temperature[ch], ch), ch);
			}
		}

		cur_fifo_ptr = next_fifo_ptr;
		
	}
}


// task of calibration
#define		CALB_PRI		10
OS_STK   	task_calb_stk[TASK_STK_SIZE];	 	/*!< Stack of 'task_init' task.	  */
static		OS_TID calb_task_id = E_CREATE_FAIL;

void task_calibration(void * pdata)
{
	uint8_t ch = (uint8_t)(pdata);
	int32_t adc;
//	int16_t temp, amp;
	int16_t amp;

	while (1)
	{
		CoTickDelay(100);		// report data per 1s
		adc = adc_acc[ch] >> WIN_POWER;
//		temp = adc2temp(adc, ch);
		amp = temp2amp(temperature[ch], ch);
		usb_printf("0x%04X, 0x%04X, 0x%X\r\n", adc, temperature[ch], amp);
	}
}

void StartCalibration(uint8_t ch, param_t param)
{
	if (E_CREATE_FAIL != calb_task_id)
	{
		usb_printf("calberation task is running\r\n");
		return;
	}

	calb_task_id = CoCreateTask(task_calibration, (void*)(ch), CALB_PRI, &task_calb_stk[TASK_STK_SIZE-1], TASK_STK_SIZE);
	if ( E_CREATE_FAIL == calb_task_id )
	{
		usb_printf("creating calberation task failed.\r\n");
	}
	else	usb_printf("calberation task %d created.\r\n", calb_task_id);
}

void EndCalibration(uint8_t ch, param_t param)
{
	if (E_CREATE_FAIL == calb_task_id)
	{
		usb_printf("no calberation task is running\r\n");
		return;
	}

	CoDelTask(calb_task_id);
	calb_task_id = E_CREATE_FAIL;
	usb_printf("calberation stopped\r\n");
}

void SetFanPower(uint8_t ch, param_t param)
{
	if (ch >= MAX_CHANNEL)
	{
		usb_printf("!01 illeagle channel.\r\n");
		return;
	}
	if (param > 0x10) 
	{
		manual_fan_power[ch] = 0xFF;
		usb_printf("channel %d auto.\r\n", ch);
	}
	else 
	{
		manual_fan_power[ch] = param;
		update_pwm(param, ch);
		usb_printf("channel %d set to %X.\r\n", ch, param);
	}
}

void read_temperature(uint8_t ch, param_t param)
{
//	int32_t adc;
//	int16_t temp;
	
//	adc = adc_acc[ch] >> WIN_POWER;
//	temp = adc2temp(adc, ch);	
	if (ch >= MAX_CHANNEL)
	{
		usb_printf("!01 illeagle channel.\r\n");
		return;
	}
	usb_printf("0x%03X\r\n", temperature[ch]);		
}
